package com.googlecode.mapperdao

import com.googlecode.mapperdao.jdbc.Setup
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.{Matchers, FunSuite}

/**
 * @author kostantinos.kougios
 *
 *         31 Oct 2011
 */
@RunWith(classOf[JUnitRunner])
class OneToManySimpleTypesSuite extends FunSuite with Matchers
{

	import OneToManySimpleTypesSuiteString._
	import OneToManySimpleTypesSuiteInt._

	val (jdbc, mapperDao, queryDao) = Setup.setupMapperDao(List(ProductEntity, ProductEntityI, TagsEntity, TagsEntityI))

	test("select") {
		createTables("create-tables-string")
		val product = Product("test", Set("tag1", "tag2", "tag3"))
		val inserted = mapperDao.insert(ProductEntity, product)
		mapperDao.select(ProductEntity, inserted.id).get should be === product
	}

	test("insert") {
		createTables("create-tables-string")
		val product = Product("test", Set("tag1", "tag2", "tag3"))
		val inserted = mapperDao.insert(ProductEntity, product)
		inserted should be === product
	}

	test("update, remove") {
		createTables("create-tables-string")
		val product = Product("test", Set("tag1", "tag2", "tag3"))
		val inserted = mapperDao.insert(ProductEntity, product)
		val up = Product(inserted.name, inserted.tags.filterNot(_ == "tag2"))
		val updated = mapperDao.update(ProductEntity, inserted, up)
		updated should be === up
		mapperDao.select(ProductEntity, inserted.id).get should be === updated
	}

	test("update, add") {
		createTables("create-tables-string")
		val product = Product("test", Set("tag1"))
		val inserted = mapperDao.insert(ProductEntity, product)
		val up = Product(inserted.name, inserted.tags ++ Set("tag2", "tag3"))
		val updated = mapperDao.update(ProductEntity, inserted, up)
		updated should be === up
		mapperDao.select(ProductEntity, inserted.id).get should be === updated
	}

	test("query") {
		createTables("create-tables-string")
		val p1 = mapperDao.insert(ProductEntity, Product("test1", Set("tag1", "tag2", "tag3")))
		val p2 = mapperDao.insert(ProductEntity, Product("test2", Set("tag10", "tag20", "tag3")))
		val p3 = mapperDao.insert(ProductEntity, Product("test3", Set("tag10", "tag20", "tag30")))

		queryDao.query(q0).toSet should be === Set(p1, p2)
	}

	test("IntEntity : update, remove") {
		createTables("create-tables-int")
		val product = ProductI("test", Set(5, 6, 7))
		val inserted = mapperDao.insert(ProductEntityI, product)
		val up = ProductI(inserted.name, inserted.tags.filterNot(_ == 6))
		val updated = mapperDao.update(ProductEntityI, inserted, up)
		updated should be === up
		mapperDao.select(ProductEntityI, inserted.id).get should be === updated
	}

	def createTables(sql: String) {
		Setup.dropAllTables(jdbc)
		val queries = Setup.queries(this, jdbc)
		queries.update(sql)
		Setup.database match {
			case "oracle" =>
				Setup.createSeq(jdbc, "ProductSeq")
			case _ =>
		}

	}
}

object OneToManySimpleTypesSuiteString
{

	case class Product(name: String, tags: Set[String])

	val TagsEntity = StringEntity.oneToMany("ProductTags", "tag")

	object ProductEntity extends Entity[Int, SurrogateIntId, Product]
	{
		val id = key("id") sequence (Setup.database match {
			case "oracle" => Some("ProductSeq")
			case _ => None
		}) autogenerated (_.id)
		val name = column("name") to (_.name)
		val tags = onetomany(TagsEntity) tostring (_.tags)

		def constructor(implicit m: ValuesMap) = new Product(name, tags) with Stored
		{
			val id: Int = ProductEntity.id
		}
	}

	// queries

	import Query._

	val pe = ProductEntity
	val te = TagsEntity

	def q0 = (
		select from pe
			join(pe, pe.tags, te)
			where te.value === "tag3"
		)
}

object OneToManySimpleTypesSuiteInt
{

	case class ProductI(name: String, tags: Set[Int])

	val TagsEntityI = IntEntity.oneToMany("ProductTagsI", "intTag")

	object ProductEntityI extends Entity[Int, SurrogateIntId, ProductI]
	{
		val id = key("id") sequence (Setup.database match {
			case "oracle" => Some("ProductSeq")
			case _ => None
		}) autogenerated (_.id)
		val name = column("name") to (_.name)
		val tags = onetomany(TagsEntityI) toint (_.tags)

		def constructor(implicit m: ValuesMap) = new ProductI(name, tags) with Stored
		{
			val id: Int = ProductEntityI.id
		}
	}

}